package jp.sourceforge.akjrcp.util.image;



//PpmDecoder - read in a PPM image
//
//Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//SUCH DAMAGE.
//
//Visit the ACME Labs Java page for up-to-date versions of this and other
//fine Java utilities: http://www.acme.com/java/



import java.io.*;
import java.awt.image.*;

/// Read in a PPM image.
//<P>
//<A HREF="/resources/classes/Acme/JPM/Decoders/PpmDecoder.java">Fetch the software.</A><BR>
//<A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
//<P>
//@see Acme.JPM.Encoders.PpmEncoder

public class PpmDecoder extends ImageDecoder
 {

 /// Constructor.
 // @param in The stream to read the bytes from.
 public PpmDecoder( InputStream in )
	{
	super( in );
	}


 private int type;
 private static final int PBM_ASCII = 1;
 private static final int PGM_ASCII = 2;
 private static final int PPM_ASCII = 3;
 private static final int PBM_RAW = 4;
 private static final int PGM_RAW = 5;
 private static final int PPM_RAW = 6;

 private int width = -1, height = -1;
 private int maxval;

 /// Subclasses implement this to read in enough of the image stream
 // to figure out the width and height.
 void readHeader( InputStream in ) throws IOException
	{
	char c1, c2;

	c1 = (char) readByte( in );
	c2 = (char) readByte( in );

	if ( c1 != 'P' )
	    throw new IOException( "not a PBM/PGM/PPM file" );
	switch ( c2 )
	    {
	    case '1':
	    type = PBM_ASCII;
	    break;
	    case '2':
	    type = PGM_ASCII;
	    break;
	    case '3':
	    type = PPM_ASCII;
	    break;
	    case '4':
	    type = PBM_RAW;
	    break;
	    case '5':
	    type = PGM_RAW;
	    break;
	    case '6':
	    type = PPM_RAW;
	    break;
	    default:
	    throw new IOException( "not a standard PBM/PGM/PPM file" );
	    }
	width = readInt( in );
	height = readInt( in );
	if ( type != PBM_ASCII && type != PBM_RAW )
	    maxval = readInt( in );
	
//	log.info(type+" "+width+"x"+height+" max="+maxval);
	}

 /// Subclasses implement this to return the width, or -1 if not known.
 int getWidth()
	{
	return width;
	}

 /// Subclasses implement this to return the height, or -1 if not known.
 int getHeight()
	{
	return height;
	}

 /// Subclasses implement this to read pixel data into the rgbRow
 // array, an int[width].  One int per pixel, no offsets or padding,
 // RGBdefault (AARRGGBB) color model
 void readRow( InputStream in, int row, int[] rgbRow ) throws IOException
	{
	int col, r, g, b;
	int rgb = 0;
	char c;

	for ( col = 0; col < width; ++col )
	    {
	    switch ( type )
		{
		case PBM_ASCII:
		c = readChar( in );
		if ( c == '1' )
		    rgb = 0xff000000;
		else if ( c == '0' )
		    rgb = 0xffffffff;
		else
		    throw new IOException( "illegal PBM bit" );
		break;
		case PGM_ASCII:
		g = readInt( in );
		rgb = makeRgb( g, g, g );
		break;
		case PPM_ASCII:
		r = readInt( in );
		g = readInt( in );
		b = readInt( in );
		rgb = makeRgb( r, g, b );
		break;
		case PBM_RAW:
		if ( readBit( in ) )
		    rgb = 0xff000000;
		else
		    rgb = 0xffffffff;
		break;
		case PGM_RAW:
		g = readByte( in );
		if ( maxval != 255 )
		    g = fixDepth( g );
		rgb = makeRgb( g, g, g );
		break;
		case PPM_RAW:
		r = readByte( in );
		g = readByte( in );
		b = readByte( in );
		if ( maxval != 255 )
		    {
		    r = fixDepth( r );
		    g = fixDepth( g );
		    b = fixDepth( b );
		    }
		rgb = makeRgb( r, g, b );
		break;
		}
	    rgbRow[col] = rgb;
	    }
	}

 /// Utility routine to read a byte.  Instead of returning -1 on
 // EOF, it throws an exception.
 private static int readByte( InputStream in ) throws IOException
	{
	int b = in.read();
	if ( b == -1 )
	    throw new EOFException();
	return b;
	}

 private int bitshift = -1;
 private int bits;

 /// Utility routine to read a bit, packed eight to a byte, big-endian.
 private boolean readBit( InputStream in ) throws IOException
	{
	if ( bitshift == -1 )
	    {
	    bits = readByte( in );
	    bitshift = 7;
	    }
	boolean bit = ( ( ( bits >> bitshift ) & 1 ) != 0 );
	--bitshift;
	return bit;
	}

 /// Utility routine to read a character, ignoring comments.
 private static char readChar( InputStream in ) throws IOException
	{
	char c;

	c = (char) readByte( in );
	if ( c == '#' )
	    {
	    do
		{
		c = (char) readByte( in );
		}
	    while ( c != '\n' && c != '\r' );
	    }

	return c;
	}

 /// Utility routine to read the first non-whitespace character.
 private static char readNonwhiteChar( InputStream in ) throws IOException
	{
	char c;

	do
	    {
	    c = readChar( in );
	    }
	while ( c == ' ' || c == '\t' || c == '\n' || c == '\r' );

	return c;
	}

 /// Utility routine to read an ASCII integer, ignoring comments.
 private static int readInt( InputStream in ) throws IOException
	{
	char c;
	int i;

	c = readNonwhiteChar( in );
	if ( c < '0' || c > '9' )
	    throw new IOException( "junk in file where integer should be" );

	i = 0;
	do
	    {
	    i = i * 10 + c - '0';
	    c = readChar( in );
	    }
	while ( c >= '0' && c <= '9' );

	return i;
	}

 /// Utility routine to rescale a pixel value from a non-eight-bit maxval.
 private int fixDepth( int p )
	{
	return ( p * 255 + maxval / 2 ) / maxval;
	}

 /// Utility routine make an RGBdefault pixel from three color values.
 private static int makeRgb( int r, int g, int b )
	{
	return 0xff000000 | ( r << 16 ) | ( g << 8 ) | b;
	}

 }

